Skip to content

Extend edit reconciliation to structured item identities, supplementary file scanning, transactional apply, and exact-path confirmation preview - #458

Merged
eXamadeus merged 1 commit into
mainfrom
julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview
Jul 24, 2026
Merged

Extend edit reconciliation to structured item identities, supplementary file scanning, transactional apply, and exact-path confirmation preview#458
eXamadeus merged 1 commit into
mainfrom
julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview

Conversation

@eXamadeus

@eXamadeus eXamadeus commented Jul 24, 2026

Copy link
Copy Markdown
Member

Why

The edit reconciliation system only understood primary asset files (skills, agents, commands). Skill companion files declared under a skill's files array, common root-level files like LICENSE and CHANGELOG.md, and the two conventional README paths (README.md and README) were invisible to the scanner and reconciler, meaning undeclared files went unnoticed and declared-but-missing files were silently ignored.

Details

Structured reconciliation item types. ReconciliationItem is now a tagged union with six variants (asset-addition, asset-missing, companion-addition, companion-missing, root-addition, root-missing) instead of two string-keyed variants. Each variant carries its own typed fields so no consumer needs to parse a colon-joined key back into structured data. reconciliationItemKey produces a stable one-way key for React lists and focus IDs only.

Resolution actions are renamed and narrowed. add-to-manifest/scaffold-template/remove-from-manifest become add/scaffold/remove/ignore. resolutionForOption is the single place that maps an option index to a resolution, making illegal item/action pairings unrepresentable.

Supplementary scanning and reconciliation. scanSkillCompanions walks a skill's directory and returns companion paths relative to the skill dir. scanCommonRootFiles checks a curated list of conventional root files (LICENSE, CHANGELOG.md, CONTRIBUTING.md, etc.). reconcileSupplementary compares these disk facts against manifest declarations and emits the appropriate addition/missing items. README paths are explicitly excluded from this path and routed to a new dedicated README panel.

README panel state. computeReadmeStates classifies each of the two conventional README paths into one of four states (present-declared, present-undeclared, declared-missing, absent-undeclared) independently. EditContext now carries a readme field alongside reconciliationItems.

Pure manifest mutation helpers. addTopLevelFile, removeTopLevelFile, addSkillCompanion, and removeSkillCompanion are pure functions that return new manifests with sorted, de-duplicated files arrays and drop empty arrays rather than leaving files: [].

Transactional apply. applyEditOperations no longer takes a separate manifest argument — the final manifest travels inside the write-manifest operation so the entire operation list drives one applyFsTransaction call. delete-asset carries explicit companionPaths so the transaction deletes only declared companions and leaves undeclared files untouched. write-file and delete-file operations handle supplementary file writes and removals. Rollback now swallows errors when restoring a pre-image that is already absent (e.g. when a parent path is not a directory).

Operation preview. previewEditOperations expands the queued operation list into exact repo-relative path lines for the confirmation and success views, including each deleted companion path individually.

EditConfirmView now receives the pending operations and renders the file-change preview before the user confirms, satisfying the "show every queued exact-path operation before Apply" requirement.

useEditSession resolution map now stores ResolvedItem (structured item + resolution) instead of a bare resolution, so buildOperations can act on the item's typed fields without re-parsing a string key. Resolutions are seeded from the wizard snapshot so they survive external-editor round-trips that unmount and remount the wizard.

Verification

New tests in edit-supplementary.test.ts cover the scanner, supplementary reconciler, README state classifier, declaration mutation helpers, transactional apply (including rollback), operation preview, and the full buildEditContext integration path. Existing integration tests are updated to use the renamed operation and item kinds.


Note

Medium Risk
Changes the edit apply contract and filesystem transaction behavior for manifest + multi-file writes; mistakes could corrupt facet projects, though rollback and tests mitigate this.

Overview
facet edit now reconciles skill companions, common root files (e.g. LICENSE), and declared-but-missing supplementary paths—not only primary skills/agents/commands. Reconciliation items are a six-variant tagged union with stable lookup keys (not parsed back from strings); resolutions are centralized as add / ignore / scaffold / remove.

README.md and README are excluded from generic reconciliation; EditContext.readme carries independent per-path states from computeReadmeStates (dedicated README panel UI is still outstanding per tasks).

Apply path: applyEditOperations(operations, rootDir) runs manifest + file ops through applyFsTransaction; the manifest lives on write-manifest. Ops include scaffold-asset, delete-asset (with explicit companionPaths so undeclared skill files survive), write-file, and delete-file. previewEditOperations drives confirm/success exact-path listings; the CLI surfaces apply failures and rollback status.

The edit session applies companion/root declaration deltas via pure manifest helpers, avoids scaffolding over on-disk asset additions, and keeps structured ResolvedItem resolutions across wizard snapshots.

Reviewed by Cursor Bugbot for commit 5eaf99e. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5eaf99e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@eXamadeus, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2af9790c-8892-4951-8865-25f54e1aabf9

📥 Commits

Reviewing files that changed from the base of the PR and between b9852b9 and 5eaf99e.

📒 Files selected for processing (21)
  • openspec/changes/support-non-asset-files/tasks.md
  • packages/cli/src/__tests__/edit-integration.test.ts
  • packages/cli/src/commands/edit/index.ts
  • packages/cli/src/tui/views/__tests__/confirm-privacy.test.tsx
  • packages/cli/src/tui/views/edit/edit-confirm-view.tsx
  • packages/cli/src/tui/views/edit/reconciliation-view.tsx
  • packages/cli/src/tui/views/edit/success-view.tsx
  • packages/cli/src/tui/views/edit/use-edit-session.ts
  • packages/cli/src/tui/views/edit/wizard.tsx
  • packages/engine/src/__tests__/edit-supplementary.test.ts
  • packages/engine/src/edit/context.ts
  • packages/engine/src/edit/declarations.ts
  • packages/engine/src/edit/operation-preview.ts
  • packages/engine/src/edit/operations.ts
  • packages/engine/src/edit/readme-state.ts
  • packages/engine/src/edit/reconcile-actions.ts
  • packages/engine/src/edit/reconcile-supplementary.ts
  • packages/engine/src/edit/scanner.ts
  • packages/engine/src/edit/types.ts
  • packages/engine/src/fs-transaction.ts
  • packages/engine/src/index.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

eXamadeus commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

onComplete(result)
if (onApply && result.outcome === 'applied') {
await onApply(result)
setDoneOperations(result.operations)
setPhase('done')

P2 Badge Do not show successful apply until the transaction succeeds

When applyEditOperations returns { ok: false } (for example, a later file write fails due to permissions or a path conflict), onApply records the error but resolves normally. This callback then unconditionally marks the edit complete and moves to the success view, so the user sees “Changes applied” before the command reports the failure after unmounting. Have the callback return an outcome and only call onComplete/enter done after a successful apply.

AGENTS.md reference: AGENTS.md:L172-L178

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +55 to +57
const result = applyFsTransaction(mutations)
if (result.ok) return { ok: true }
return { ok: false, failedPath: result.failedPath, reason: result.reason, rollbackOk: result.rollback.ok }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Catch preimage read failures in transactional edit apply

A declared companion or supplementary path can be a directory on disk: the scanners treat it as missing because they scan only files, so choosing Scaffold queues a write at that path. applyFsTransaction captures preimages before its mutation try and readFileSync on that directory throws, causing this result-shaped API to reject instead of returning { ok: false }; the edit command only handles false results. Convert preimage-capture failures into the transaction result as well.

AGENTS.md reference: AGENTS.md:L172-L178

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/tui/views/edit/wizard.tsx
* `files` array (repo-relative paths); `skill` is a skill descriptor's `files`
* array (paths relative to that skill's directory).
*/
export type DeclarationSite = { kind: 'root' } | { kind: 'skill'; skill: string }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported declaration type

Low Severity

The DeclarationSite type in packages/engine/src/edit/types.ts is exported but unused. Its role for tagging declaration sites is already handled by ReconciliationItem variants, making it redundant API surface.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 28c186f. Configure here.

Comment thread packages/engine/src/edit/context.ts
Comment thread packages/cli/src/tui/views/edit/use-edit-session.ts
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

Greptile Summary

This PR substantially extends the facet edit engine: ReconciliationItem grows from two string-keyed variants to six typed variants (asset, companion, root × addition/missing), resolution actions are renamed and narrowed to a resolutionForOption single source, supplementary scanning (skill companions and curated root files) and reconciliation are added, README paths are split to a dedicated panel state, and the entire apply path is now a single applyFsTransaction call with structured rollback.

  • Engine: New scanners (scanSkillCompanions, scanCommonRootFiles), pure manifest-mutation helpers (addTopLevelFile etc.), reconcileSupplementary, computeReadmeStates, previewEditOperations, and the transactional applyEditOperations (no longer takes a separate manifest argument).
  • CLI/TUI: useEditSession stores ResolvedItem and seeds from wizard snapshots; EditConfirmView renders an exact-path preview before confirm; apply errors surface on stderr with a non-zero exit.
  • Tests: edit-supplementary.test.ts covers scanners, reconciler, README classifier, declaration mutations, transactional apply with rollback, and the full buildEditContext integration path.

Confidence Score: 4/5

Safe to merge with one known UX regression in the wizard: after a failed apply with rollback the success view is still shown before Ink exits and the error appears on stderr.

The transactional apply, rollback, and supplementary reconciliation logic are well-designed and thoroughly tested. The main concern is in handleConfirm in wizard.tsx, which unconditionally transitions to the done phase after onApply returns regardless of outcome, because the new closure-based error-surfacing pattern gives the wizard no signal to distinguish success from failure.

packages/cli/src/tui/views/edit/wizard.tsx — the handleConfirm callback does not distinguish a successful apply from a rolled-back one before calling setPhase('done').

Reviews (3): Last reviewed commit: "Extend edit reconciliation to structured..." | Re-trigger Greptile

Comment thread packages/engine/src/edit/reconcile-actions.ts
Comment on lines 134 to 137
if (phase === 'confirmation') {
const pending = buildResult(form)
const operations = pending.outcome === 'applied' ? pending.operations : []
return (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 buildResult(form) is called as a plain expression in the render body of the confirmation branch. Every focus change (arrow key between the two buttons) triggers a re-render that recomputes buildManifest, applySupplementaryDeltas, and buildOperations in full. useMemo would pin the result to the form/resolutions values that actually drive it, which are stable during the confirmation phase.

Suggested change
if (phase === 'confirmation') {
const pending = buildResult(form)
const operations = pending.outcome === 'applied' ? pending.operations : []
return (
const confirmationOperations = useMemo(() => {
if (phase !== 'confirmation') return []
const pending = buildResult(form)
return pending.outcome === 'applied' ? pending.operations : []
}, [phase, buildResult, form])
if (phase === 'confirmation') {
const operations = confirmationOperations
return (

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread packages/engine/src/edit/reconcile-actions.ts
@eXamadeus
eXamadeus force-pushed the julian/07-23-add_readme_scaffolding_to_create_wizard_default-on_toggle_seeded_template_authored-content_preservation_and_atomic_write_with_rollback branch from d301e37 to b3293d1 Compare July 24, 2026 04:03
@eXamadeus
eXamadeus force-pushed the julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview branch from 28c186f to a27000a Compare July 24, 2026 04:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a27000a338

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +31 to +35
if (!applied.ok) {
applyError = `Failed to apply changes at ${applied.failedPath}: ${applied.reason}${
applied.rollbackOk ? ' (rolled back)' : ' (rollback incomplete)'
}`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate failed edit applies back to the wizard

When a filesystem mutation fails (for example, a scaffold target has a file as its parent), this callback records applyError but still fulfills its Promise<void>. EditWizard.handleConfirm consequently marks the run complete and renders “Changes applied.” before the command prints the failure and exits 1. Return the apply result (or throw/handle a failure at the wizard boundary) so the success view and completion state are reached only after a successful transaction.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a27000a. Configure here.

/** The two option labels for an item, in index order. */
export function optionLabelsFor(item: ReconciliationItem): [string, string] {
return isAdditionItem(item) ? ['Add to manifest', 'Ignore for now'] : ['Scaffold template', 'Remove from manifest']
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading supplementary scaffold labels

Medium Severity

optionLabelsFor labels every missing item as Scaffold template, including companion-missing and root-missing. Those resolutions write empty bytes at the declared path, not an asset template, so the reconciliation UI misstates what Apply will do.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a27000a. Configure here.

Comment thread packages/cli/src/tui/views/edit/use-edit-session.ts

eXamadeus commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Merge activity

  • Jul 24, 4:47 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 24, 4:57 AM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 24, 4:57 AM UTC: @eXamadeus merged this pull request with Graphite.

@eXamadeus
eXamadeus changed the base branch from julian/07-23-add_readme_scaffolding_to_create_wizard_default-on_toggle_seeded_template_authored-content_preservation_and_atomic_write_with_rollback to graphite-base/458 July 24, 2026 04:54
@eXamadeus
eXamadeus changed the base branch from graphite-base/458 to main July 24, 2026 04:55
…ry file scanning, transactional apply, and exact-path confirmation preview
@eXamadeus
eXamadeus force-pushed the julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview branch from a27000a to 5eaf99e Compare July 24, 2026 04:56
@eXamadeus
eXamadeus merged commit 4bd6831 into main Jul 24, 2026
7 checks passed
@eXamadeus
eXamadeus deleted the julian/07-23-extend_edit_reconciliation_to_structured_item_identities_supplementary_file_scanning_transactional_apply_and_exact-path_confirmation_preview branch July 24, 2026 04:57

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5eaf99e920

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +70 to +74
try {
rmSync(preimage.path, { force: true })
} catch {
// Already absent for all practical purposes.
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report rollback cleanup failures

Do not swallow every rmSync error here. If a transaction has already created a previously absent file and a later mutation fails, a cleanup failure such as EACCES (for example, after concurrent permission changes) leaves that new file behind, yet rollbackApplied reports rollback.ok: true and the CLI tells the user the changes were rolled back. Only suppress errors that prove the path is already absent (such as ENOENT/the intended non-directory case), and let other errors reach rollbackApplied.

AGENTS.md reference: AGENTS.md:L172-L178

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant